#include "../include/Playground.h"

using namespace godot;

Playground::Playground() {
	undoStack = std::unique_ptr<std::deque<unsigned short>>(new std::deque<unsigned short>());
	redoStack = std::unique_ptr<std::stack<unsigned short>>(new std::stack<unsigned short>());
	cells = std::unique_ptr<std::vector<Node*>>(new std::vector<Node*>());
}

void Playground::_register_methods() {
	register_signal<Playground>("backToMenu");
	
	register_method("setup", &Playground::setup);
	register_method("_ready", &Playground::_ready);
	register_method("_process", &Playground::_process);
	register_method("_timeout", &Playground::_timeout);
	register_method("_on_AcceptDialog_confirmed", &Playground::_on_AcceptDialog_confirmed);
	register_method("_on_CheckButton_pressed", &Playground::_on_CheckButton_pressed);
	register_method("_on_PauseButton_pressed", &Playground::_on_PauseButton_pressed);
	register_method("_on_UndoButton_pressed", &Playground::_on_UndoButton_pressed);
	register_method("_on_RedoButton_pressed", &Playground::_on_RedoButton_pressed);
	register_method("_on_ReturnButton_pressed", &Playground::_on_ReturnButton_pressed);
	register_method("_update_history", &Playground::_update_history);
	
}

void Playground::_init() {
	
}
void Playground::_ready() {
	get_node<AcceptDialog>("AcceptDialog")->connect("confirmed", this, "_on_AcceptDialog_confirmed");
	get_node<TextureButton>("HUD/PauseButton")->connect("pressed", this, "_on_PauseButton_pressed");
	get_node<TextureButton>("HUD/CheckButton")->connect("pressed", this, "_on_CheckButton_pressed");
	get_node<TextureButton>("HUD/UndoButton")->connect("pressed", this, "_on_UndoButton_pressed");
	get_node<TextureButton>("HUD/RedoButton")->connect("pressed", this, "_on_RedoButton_pressed");
	get_node<Button>("HUD/ControlTab/VBoxContainer/ReturnButton")->connect("pressed", this, "_on_ReturnButton_pressed");
	
	get_node<BaseButton>("HUD/UndoButton")->set_disabled(true);
	get_node<BaseButton>("HUD/RedoButton")->set_disabled(true);
	
	GridContainer* grid = get_node<GridContainer>("PlayArea/CenterContainer/GridContainer");
	grid->set_columns(game->columns);
	Ref<PackedScene> cellRef = ResourceLoader::get_singleton()->load("res://scenes/Cell.tscn");
	for (std::vector<unsigned short>::iterator i = game->matrix.begin(); i != game->matrix.end(); ++i) {
		Node* cell = cellRef->instance();
		Array params;
		cell->call("setup", *i, std::distance(game->matrix.begin(), i));
		grid->add_child(cell);
		params.push_back(std::distance(game->matrix.begin(), i));
		cell->connect("updateHistory", this, "_update_history", params);
		cells->push_back(cell);
	}
	for (std::set<unsigned short>::iterator i = game->solution.begin(); i != game->solution.end(); ++i) {
		Godot::print(std::to_string(*i).c_str());
	}
}

void Playground::setup(const unsigned short rows, const unsigned short cols) {
	game = std::unique_ptr<HitoriGenerator::HitoriBoard>(new HitoriGenerator::HitoriBoard(rows, cols));
}

void Playground::_process(float delta) {
	time++;
}
void Playground::_timeout() {
	get_node<BaseButton>("HUD/CheckButton")->set_disabled(false);
}
void Playground::_update_history(const unsigned short index) {
	if (undoStack->size() >= 256) {
		undoStack->pop_front();
	}
	undoStack->push_back(index);
	get_node<BaseButton>("HUD/UndoButton")->set_disabled(false);
	redoStack.reset(new std::stack<unsigned short>());
	get_node<BaseButton>("HUD/RedoButton")->set_disabled(true);
}

void Playground::_on_AcceptDialog_confirmed() {
	get_parent()->call("_play_click1");
	get_tree()->set_pause(false);
}
void Playground::_on_CheckButton_pressed() {
	get_parent()->call("_play_click1");
	if (__is_solved()) {
		get_tree()->set_pause(true);
		std::string output = std::to_string(((real_t)((int64_t)(((real_t)time/60)*1000+0.5))/1000));
		for (size_t i = output.length() - 1, point = output.find_first_of('.'); i > point + 3; --i) {
			output.pop_back();
		}
		get_node<Label>("AcceptDialog/Label2")->set_text((String)"You have solved the board in\n" + (String)(output.c_str()) + (String)" seconds.");
		get_node("AcceptDialog")->call("show");
	} else {
		get_node<BaseButton>("HUD/CheckButton")->set_disabled(true);
		get_tree()->create_timer(3.0)->connect("timeout", this, "_timeout");;
	}
}
void Playground::_on_PauseButton_pressed() {
	get_parent()->call("_play_click1");
	if (hudIsRevealed) {
		get_node<Control>("HUD")->set_position(get_node<Control>("HUD")->get_position() + Vector2(-114, 0));
		get_node<Control>("PlayArea")->call("hide");
		get_tree()->set_pause(true);
		get_node("HUD/PauseButton")->set_pause_mode(2);
		hudIsRevealed = false;
	} else {
		get_node<Control>("HUD")->set_position(get_node<Control>("HUD")->get_position() + Vector2(114, 0));
		get_node<Control>("PlayArea")->call("show");
		get_tree()->set_pause(false);
		get_node("HUD/PauseButton")->set_pause_mode(0);
		hudIsRevealed = true;
	}
	((AtlasTexture*)(get_node<TextureButton>("HUD/PauseButton")->get_normal_texture().ptr()))->set_region(Rect2((hudIsRevealed) ? 0 : 80, 80, 40, 40));
	((AtlasTexture*)(get_node<TextureButton>("HUD/PauseButton")->get_pressed_texture().ptr()))->set_region(Rect2(((hudIsRevealed) ? 0 : 80) + 40, 80, 40, 40));
}
void Playground::_on_UndoButton_pressed() {
	get_parent()->call("_play_click2");
	__undo();
}
void Playground::_on_RedoButton_pressed() {
	get_parent()->call("_play_click2");
	__redo();
}
void Playground::_on_ReturnButton_pressed() {
	get_parent()->call("_play_click1");
	get_tree()->set_pause(false);
	call("emit_signal", "backToMenu");
}

bool Playground::__is_solved() const {
	Array array = get_node<GridContainer>("PlayArea/CenterContainer/GridContainer")->get_children();
	unsigned short state;
	for (unsigned short i = 0; i < array.size() ; ++i) {
		state = Object::cast_to<Node>(array[i])->get("state");
		if (game->solution.find(i) != game->solution.end()) {
			if (state != 1) {
				return false;
			}
		} else {
			if (state == 1) {
				return false;
			}
		}
	}
	return true;
}
void Playground::__undo() {
	unsigned short index = undoStack->back();
	(*cells)[index]->call("_untoggle");
	redoStack->push(index);
	get_node<BaseButton>("HUD/RedoButton")->set_disabled(false);
	undoStack->pop_back();
	
	if (undoStack->size() == 0) {
		get_node<BaseButton>("HUD/UndoButton")->set_disabled(true);
	}
}
void Playground::__redo() {
	unsigned short index = redoStack->top();
	(*cells)[index]->call("_toggle");
	undoStack->push_back(index);
	get_node<BaseButton>("HUD/UndoButton")->set_disabled(false);
	redoStack->pop();
	
	if (redoStack->size() == 0) {
		get_node<BaseButton>("HUD/RedoButton")->set_disabled(true);
	}
}